package com.tool.cn.util;

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.tool.cn.ToolJob;
	
/**
 * 加密工具类
 * 加密分对称加密和非对称加密，对称加密使用一个密匙，安全性低。非对称加密，分共匙和私匙，安全性更高，推荐非对称加密RSA
 * 对称加密：DES、3DES、AES
 * 非对称加密：RSA
 * 参考资料：https://blog.csdn.net/dengjili/article/details/85862201
 * @author wangzh 2016年9月21日 下午5:55:48
 */
public final class EncryptUtil {
	
	private static final Log log = LogFactory.getLog(ToolJob.class);
	
	
	private static final String KEY_ALGORITHM = "DES";
	private static final String DEFAULT_CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding";// 默认的加密算法

	/**
	 * DES 加密操作
	 * 
	 * @return 返回Base64转码后的加密数据
	 */
	public static String encrypt(String content, String key) {
		try {
			Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 创建密码器

			byte[] byteContent = content.getBytes("utf-8");

			cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));// 初始化为加密模式的密码器

			byte[] result = cipher.doFinal(byteContent);// 加密

			return Base64.getEncoder().encodeToString(result);// 通过Base64转码返回
		} catch (Exception e) {
			log.error(e.toString(),e);
		}

		return null;
	}

	/**
	 * DES 解密操作
	 */
	public static String decrypt(String content, String key) {

		try {
			// 实例化
			Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);

			// 使用密钥初始化，设置为解密模式
			cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key));

			// 执行操作
			byte[] result = cipher.doFinal(Base64.getDecoder().decode(content));

			return new String(result, "utf-8");
		} catch (Exception e) {
			log.error(e.toString(),e);
		}

		return null;
	}

	/**
	 * 生成加密秘钥
	 *
	 * @return
	 */
	private static SecretKeySpec getSecretKey(final String key) {
		// 返回生成指定算法密钥生成器的 KeyGenerator 对象
		KeyGenerator kg = null;

		try {
			kg = KeyGenerator.getInstance(KEY_ALGORITHM);

			// DES 要求密钥长度为 56
			kg.init(56, new SecureRandom(key.getBytes()));

			// 生成一个密钥
			SecretKey secretKey = kg.generateKey();

			return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);// 转换为DES专用密钥
		} catch (NoSuchAlgorithmException e) {
			log.error(e.toString(),e);
		}

		return null;
	}
	
	
	//AES 128位加密和解密
	/**
     * aes加密
     *
     * @param content
     * @param strKey
     * @return
     * @throws Exception
     */
    public static byte[] aesEncrypt(String content, String strKey) throws Exception {
        SecretKeySpec skeySpec = getKey(strKey);
        // 加密算法AEC，加密模式CBC，数据填充模式PKCS7（PKCS5Padding）
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
      //设置IV
        IvParameterSpec iv = new IvParameterSpec("0102030405060708".getBytes());
        //加密模式
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        return cipher.doFinal(content.getBytes());
    }
    
    
    /**
     * aes解密
     *
     * @param strKey
     * @param content
     * @return
     * @throws Exception
     */
    public static String aesDecrypt(byte[] content, String strKey) throws Exception {
        SecretKeySpec skeySpec = getKey(strKey);
        // 加密算法AEC，加密模式CBC，数据填充模式PKCS7（PKCS5Padding）
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
      //设置IV
        IvParameterSpec iv = new IvParameterSpec("0102030405060708".getBytes());
       //解密模式
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] original = cipher.doFinal(content);
        String originalString = new String(original);
        return originalString;
    }
    
    /**
     * 效验128位aes密匙
     * @param strKey
     * @return
     * @throws Exception
     */
    private static SecretKeySpec getKey(String strKey) throws Exception {
        byte[] arrBTmp = strKey.getBytes();
        byte[] arrB = new byte[16]; 
        for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
            arrB[i] = arrBTmp[i];
        }
        SecretKeySpec skeySpec = new SecretKeySpec(arrB, "AES");
        return skeySpec;
    }
	
	
	/**
	 * 加密算法RSA
	 */
	public static final String RSA = "RSA";

	/**
	 * 签名算法
	 */
	public static final String SIGNATURE_ALGORITHM = "MD5withRSA";

	/**
	 * 获取公钥的key
	 */
	private static final String PUBLIC_KEY = "RSAPublicKey";

	/**
	 * 获取私钥的key
	 */
	private static final String PRIVATE_KEY = "RSAPrivateKey";

	/**
	 * RSA最大加密明文大小
	 */
	private static final int MAX_ENCRYPT_BLOCK = 117;

	/**
	 * RSA最大解密密文大小
	 */
	private static final int MAX_DECRYPT_BLOCK = 128;

	/**
	 * 用私钥对信息生成数字签名
	 * @param data 已加密数据
	 * @param privateKey 私钥(BASE64编码)
	 */
	public static String sign(byte[] data, String privateKey) throws Exception {
		byte[] keyBytes = Base64Util.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(RSA);
		PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initSign(privateK);
		signature.update(data);
		return Base64Util.encode(signature.sign());
	}

	/**
	 * 校验数字签名
	 * @param data 已加密数据
	 * @param publicKey 公钥(BASE64编码)
	 * @param sign 数字签名
	 *
	 */
	public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
		byte[] keyBytes = Base64Util.decode(publicKey);
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(RSA);
		PublicKey publicK = keyFactory.generatePublic(keySpec);
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initVerify(publicK);
		signature.update(data);
		return signature.verify(Base64Util.decode(sign));
	}

	/**
	 * 私钥解密
	 * @param encryptedData 已加密数据
	 * @param privateKey 私钥(BASE64编码)
	 */
	public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
		byte[] keyBytes = Base64Util.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(RSA);
		Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, privateK);
		int inputLen = encryptedData.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段解密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
				cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_DECRYPT_BLOCK;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return decryptedData;
	}

	/**
	 * 公钥解密
	 * @param encryptedData 已加密数据
	 * @param publicKey 公钥(BASE64编码)
	 */
	public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception {
		byte[] keyBytes = Base64Util.decode(publicKey);
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(RSA);
		Key publicK = keyFactory.generatePublic(x509KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, publicK);
		int inputLen = encryptedData.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段解密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
				cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_DECRYPT_BLOCK;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return decryptedData;
	}

	/**
	 * 公钥加密
	 * @param data 源数据
	 * @param publicKey 公钥(BASE64编码)
	 */
	public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
		byte[] keyBytes = Base64Util.decode(publicKey);
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(RSA);
		Key publicK = keyFactory.generatePublic(x509KeySpec);
		// 对数据加密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, publicK);
		int inputLen = data.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段加密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
				cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(data, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_ENCRYPT_BLOCK;
		}
		byte[] encryptedData = out.toByteArray();
		out.close();
		return encryptedData;
	}

	/**
	 * 私钥加密
	 * @param data 源数据
	 * @param privateKey 私钥(BASE64编码)
	 */
	public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception {
		byte[] keyBytes = Base64Util.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(RSA);
		Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, privateK);
		int inputLen = data.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段加密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
				cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(data, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_ENCRYPT_BLOCK;
		}
		byte[] encryptedData = out.toByteArray();
		out.close();
		return encryptedData;
	}

	/**
	 * 生成密钥对(公钥和私钥)
	 */
	public static Map<String, Object> genKeyPair() throws Exception {
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(RSA);
		keyPairGen.initialize(1024);
		KeyPair keyPair = keyPairGen.generateKeyPair();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		Map<String, Object> keyMap = new HashMap<String, Object>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}

	/**
	 * 获取私钥
 	 * @param keyMap 密钥对
	 */
	public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
		Key key = (Key) keyMap.get(PRIVATE_KEY);
		return Base64Util.encode(key.getEncoded());
	}

	/**
	 * 获取公钥
	 * @param keyMap 密钥对
	 */
	public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
		Key key = (Key) keyMap.get(PUBLIC_KEY);
		return Base64Util.encode(key.getEncoded());
	}

	public static void main(String[] args) throws Exception {
		
		System.out.println("---------Des对称加密-----------------");
		String content = "hello,您好，李四，我喜欢你，这个内容加密了，别人不知道的";
		String key = "sde@5f98H*^hsff%dfs$r344&df8543*er";
		System.out.println("原始:" + content);
		String s1 = EncryptUtil.encrypt(content, key);
		System.out.println("加密后:" + s1);
		System.out.println("解密:" + EncryptUtil.decrypt(s1, key));
		
		
		System.out.println("---------RSA非对称加密-----------------");
		Map<String, Object> keyPairs = EncryptUtil.genKeyPair();
		String privateKey = EncryptUtil.getPrivateKey(keyPairs);
		String publicKey = EncryptUtil.getPublicKey(keyPairs);

//		System.out.println("私钥：" + privateKey);
//		System.out.println("公钥：" + publicKey);

		// 公钥加密，私钥解密
		content = "hello,您好，李四，我喜欢你，这个内容加密了，别人不知道的";
		System.out.println("公匙："+publicKey);
		System.out.println("加密前："+content);
		
		byte[] encryptedData = EncryptUtil.encryptByPublicKey(content.getBytes(Charset.defaultCharset()), publicKey);
		System.out.println("加密后："+ByteUtil.getHexString(encryptedData));
		byte[] decryptedData = EncryptUtil.decryptByPrivateKey(encryptedData, privateKey);
		System.out.println("解密后："+new String(decryptedData));

		// 数字签名验证
		// 私钥签名，生产一个key
		String sign = EncryptUtil.sign(encryptedData, privateKey);
		// 公钥验证
		boolean verify = EncryptUtil.verify(encryptedData, publicKey, sign);
		System.out.println("内容是否安全：" + verify);

		// 私钥加密，公钥解密
		content = "私密匙加密";
		System.out.println("私匙："+privateKey);
		System.out.println("加密前："+content);
		byte[] encryptedData2 = EncryptUtil.encryptByPrivateKey(content.getBytes(Charset.defaultCharset()), privateKey);
		String res=ByteUtil.getHexString(encryptedData2);
		System.out.println("加密后："+res);
		
		byte[] re=ByteUtil.hexStringToBytes(res);
		
		if (Arrays.equals(encryptedData2, re)){                    
	          System.out.println("√");        
	       }
		
		byte[] decryptedData2 = EncryptUtil.decryptByPublicKey(re, publicKey);
		System.out.println("解密后："+new String(decryptedData2));
	}

	
}
